package ru.nsu.fit.iconveyor.engine.channel;

import ru.nsu.fit.iconveyor.conveyor.Conveyor;
import ru.nsu.fit.iconveyor.conveyor.channel.Channel;
import ru.nsu.fit.iconveyor.conveyor.channel.ChannelDataProvider;
import ru.nsu.fit.iconveyor.engine.exception.ChannelOverflowException;

import java.util.HashMap;
import java.util.Map;

/**
 * Data provider which creates channels for each channel id in specified conveyor with specified channel factory.
 */
public class ConveyorChannelDataProvider implements ChannelDataProvider {
    private final Map<String, Channel> channelMap;

    /**
     * Creates ConveyorChannelDataProvider instance with channels for each channel id in specified conveyor
     * with specified channel factory.
     * @param conveyor
     * @param channelFactory
     */
    public ConveyorChannelDataProvider(Conveyor conveyor, ChannelFactory channelFactory) {
        if(conveyor == null || conveyor.getChannelIds() == null || channelFactory == null) {
            throw new IllegalArgumentException();
        }
        channelMap = new HashMap<String, Channel>(conveyor.getChannelIds().size());
        for(String channelId : conveyor.getChannelIds()) {
            if(channelId == null) {
                throw new IllegalArgumentException();
            }
            channelMap.put(channelId, channelFactory.newChannel(channelId));
        }
    }

    @Override
    public <T> T pollFromChannel(String channelId, Class<T> dataClass) {
        Object result = channelMap.get(channelId).poll();
        if(!dataClass.isAssignableFrom(result.getClass())) {
            throw new IllegalStateException("invalid class of data in channel = " + channelId + " input class = " + dataClass.getName() + " base class = " + result.getClass().getName());
        }
        return (T)result;
    }

    @Override
    public boolean isChannelEmpty(String channelId) {
        return channelMap.get(channelId).isEmpty();
    }

    @Override
    public <T> T peekFromChannel(String channelId, Class<T> dataClass) {
        Object result = channelMap.get(channelId).peek();
        if(!dataClass.isAssignableFrom(result.getClass())) {
            throw new IllegalStateException("invalid class of data in channel = " + channelId + " input class = " + dataClass.getName() + " base class = " + result.getClass().getName());
        }
        return (T)result;
    }

    @Override
    public <T> void addToChannel(String channelId, T data) throws ChannelOverflowException {
        channelMap.get(channelId).add(data);
    }
}
